/***********************************************************************************
 *
 * Copyright (c) 2003, 2004, 2005, 2006 The Sakai Foundation. 
 * @author Mustansar@rice.edu
 * 
 * Licensed under the Educational Community License, Version 1.0 (the "License"); 
 * you may not use this file except in compliance with the License. 
 * You may obtain a copy of the License at
 * 
 *      http://www.opensource.org/licenses/ecl1.php
 * 
 * Unless required by applicable law or agreed to in writing, software 
 * distributed under the License is distributed on an "AS IS" BASIS, 
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
 * See the License for the specific language governing permissions and 
 * limitations under the License.
 *
 **********************************************************************************/
package org.sakaiproject.calendar.impl;

import java.util.GregorianCalendar;
import java.util.Stack;
import java.util.TimeZone;
import java.util.Vector;
import java.util.List;

import org.sakaiproject.time.api.Time;
import org.sakaiproject.time.api.TimeRange;
import org.sakaiproject.time.cover.TimeService;
import org.sakaiproject.time.api.TimeBreakdown;

import org.sakaiproject.util.CalendarUtil;

import org.w3c.dom.Document;
import org.w3c.dom.Element;

/**
* <p>MWFRecurrenceRule is a time range generating rule that is based on a weekly recurrence.</p>
* <p>repaeting Mondays Wendnesday and Fridays</p>
* <p>The recurrences happen on the same day-of-week, at the same time as the prototype.</p>
* <p>TODO: support changing the day-of-week of recurrences -Mustansar</p>
*/
public class MWFRecurrenceRule extends RecurrenceRuleBase
{
	/** The unique type / short frequency description. */
	public final static String FREQ = "MWF";
	private CalendarUtil calUtil = null;
	/**
	* Construct.
	*/
	public MWFRecurrenceRule()
	{
		super();
		calUtil = new CalendarUtil();
	}	// MWFRecurrenceRule
	/**
	* Construct with no  limits.
	* @param interval Every this many number of weeks: 1 would be weekly.
	*/
	public MWFRecurrenceRule(int interval)
	{
		super(interval);
		calUtil = new CalendarUtil();
	}	//MWFRecurrenceRule

	/**
	* Construct with count limit.
	* @param interval Every this many number of weeks: 1 would be weekly.
	* @param count For this many occurrences - if 0, does not limit.
	*/
	public MWFRecurrenceRule(int interval, int count)
	{
		super(interval, count);
		calUtil = new CalendarUtil();
	}	// MWFRecurrenceRule
	/**
	* Construct with time limit.
	* @param interval Every this many number of weeks: 1 would be weekly.
	* @param until No time ranges past this time are generated - if null, does not limit.
	*/
	public MWFRecurrenceRule(int interval, Time until)
	{
		super(interval, until);	
		calUtil = new CalendarUtil();
	}	// MWFRecurrenceRule
	/**
	* Serialize the resource into XML, adding an element to the doc under the top of the stack element.
	* @param doc The DOM doc to contain the XML (or null for a string return).
	* @param stack The DOM elements, the top of which is the containing element of the new "resource" element.
	* @return The newly added element.
	*/
	public Element toXml(Document doc, Stack stack)
	{
		// add the "rule" element to the stack'ed element
		Element rule = doc.createElement("rule");
		((Element)stack.peek()).appendChild(rule);
		// set the class name - old style for CHEF 1.2.10 compatibility
		rule.setAttribute("class", "org.chefproject.osid.calendar.MWFRecurrenceRule");
		// set the rule class name w/o package, for modern usage
		rule.setAttribute("name", "MWFRecurrenceRule");
		// Do the base class part.
		setBaseClassXML(rule);
		return rule;
	}	// toXml

	/* (non-Javadoc)
	 * @see org.chefproject.service.calendar.RecurrenceRuleBase#getRecurrenceType()
	 */
	protected int getRecurrenceType()
	{
		return GregorianCalendar.WEEK_OF_MONTH;	
	}
   
	/**
	 * {@inheritDoc}
	 */
	public String getFrequencyDescription()
	{
		return rb.getFormattedMessage("set.MWF.fm", calUtil.getDayOfWeekName(1), calUtil.getDayOfWeekName(3), calUtil.getDayOfWeekName(5));
	}
   
	/**
	 * {@inheritDoc}
	 */
	public String getFrequency()
	{
		return FREQ;
	}
   
   
	
	/**
	* Return a List of all RecurrenceInstance objects generated by this rule within the given time range, based on the
	* prototype first range, in time order.
	* @param prototype The prototype first TimeRange.
	* @param range A time range to limit the generated ranges.
	* @param timeZone The time zone to use for displaying times.
	* %%% Note: this is currently not implemented, and always uses the "local" zone.
	* @return a List of RecurrenceInstance generated by this rule in this range.
	*/
	public List generateInstances(TimeRange prototype, TimeRange range, TimeZone timeZone)
	{
		TimeBreakdown startBreakdown = prototype.firstTime().breakdownLocal();			
		List rv = new Vector();
		GregorianCalendar startCalendarDate = TimeService.getCalendar(TimeService.getLocalTimeZone(),0,0,0,0,0,0,0);	
		startCalendarDate.set(
									 startBreakdown.getYear(),
									 startBreakdown.getMonth() - 1, 
									 startBreakdown.getDay(), 
									 startBreakdown.getHour(),	
									 startBreakdown.getMin(), 
									 startBreakdown.getSec());	 //may have to move this line ahead 
		
		GregorianCalendar nextCalendarDate = (GregorianCalendar) startCalendarDate.clone();	
		if(startCalendarDate.get(GregorianCalendar.DAY_OF_WEEK)%2!=0 ){
			if(startCalendarDate.get(GregorianCalendar.DAY_OF_WEEK)==7){ 
				startCalendarDate.add(java.util.Calendar.DAY_OF_MONTH, 2);
			}
			else if (startCalendarDate.get(GregorianCalendar.DAY_OF_WEEK)==6) {
				startCalendarDate.add(java.util.Calendar.DAY_OF_MONTH, 3);
			}
			else	{
				startCalendarDate.add(java.util.Calendar.DAY_OF_MONTH, 1);
			} 
		}
		
		nextCalendarDate = (GregorianCalendar) startCalendarDate.clone();
		int currentCount = 1;
		int hitCount=1;		//counts tth/mwf occurences when getCount()>0
		do
		{	
			Time nextTime = TimeService.newTime(nextCalendarDate);
			// is this past count?
			if ((getCount() > 0) && (hitCount > getCount()))
				break; 
			// is this past until?
			if ((getUntil() != null) && isAfter(nextTime, getUntil()) )
				break;
			
			TimeRange nextTimeRange = TimeService.newTimeRange(nextTime.getTime(), prototype.duration());
			
			// Is this out of the range?
			if (isOverlap(range, nextTimeRange))
			{
				TimeRange eventTimeRange = null;
				
				// Single time cases require special handling.
				if ( prototype.isSingleTime() )
				{
					eventTimeRange = TimeService.newTimeRange(nextTimeRange.firstTime());
				}
				else
				{
					eventTimeRange = TimeService.newTimeRange(nextTimeRange.firstTime(), nextTimeRange.lastTime(), true, false);
				}
				
				// use this one
				rv.add(new RecurrenceInstance(eventTimeRange, currentCount));
			}
			
			// if next starts after the range, stop generating I have added an extra condition here to test TTH/MWF Number of times repetition 
			else if (isAfter(nextTime, range.lastTime()) ){ 
				break;
			}
         
			do{
				nextCalendarDate = (GregorianCalendar) startCalendarDate.clone();
				nextCalendarDate.add(java.util.Calendar.DAY_OF_MONTH, currentCount);//"1" is the recurrence type but here thing are confusing
				if((getInterval()>1&&(nextCalendarDate.get(GregorianCalendar.DAY_OF_WEEK)==7))){		
					nextCalendarDate.add(java.util.Calendar.DAY_OF_MONTH, ((getInterval()-1)*7)+currentCount);//"1"is the supposed recuurence Type 
					currentCount+=((getInterval()-1)*7)+1;
				}
				else {
					nextCalendarDate.add(java.util.Calendar.DAY_OF_MONTH, 1);
					currentCount++;
				}	
			} while((nextCalendarDate.get(GregorianCalendar.DAY_OF_WEEK)%2!=0));
         
			hitCount++;
		} while (true);
		
		return rv;
	}
}	// MWFRecurrenceRule



